'''Relative filename globbing using generators.

The purppose here is to simplify the use of a common path in globs, and
use generators to return the results.

Heavily inspired by the dist glob module :p

# LICENSE ....................................................................
    This file is part of DataK.

    DataK is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DataK is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DataK.  If not, see <http://www.gnu.org/licenses/>.
# ............................................................................

'''
__version__ = "0.1"
__author__ = "Damien Coureau"
__copyright__ = "Copyright (C) 2009 Damien Coureau"
__license__ = "GPL" #see file 'COPYING' for licence information

import os
import fnmatch
import re

def get_under( path, under=None ):
    """Return the part of path relative to under."""
    return under and path[len(under):].rstip('/') or path
    
def uglob(pattern, under=None):
    """Resolve the 'fnmatch' pattern by yielding pathnames.
    
    If not None, under is the path under which pattern produces pathnames.
    Yielded pathnames will be relative to it.
    
    >>> [ p for p in glob( '/tmp', 'dee/*.bkp' ) ]
    ['dee/spam.bkp', 'dee/egg.bkp']
    
    """

    if under is None:
        under = os.curdir

    if not has_magic(pattern):
        if os.path.lexists(os.join(under, pattern)):
            yield pattern
        return
    dirname, basename = os.path.split(pattern)
    if not dirname:
        for name in glob1(under, basename):
            yield name
        return
    if has_magic(dirname):
        dirs = uglob(os.path.join(under,dirname), under)
    else:
        dirs = [dirname]
    if has_magic(basename):
        glob_in_dir = glob1
    else:
        glob_in_dir = glob0
    for dirname in dirs:
        for name in glob_in_dir(os.path.join(under,dirname), basename):
            yield os.path.join(dirname, name)

# These 2 helper functions non-recursively glob inside a literal directory.
# They return a list of basenames. `glob1` accepts a pattern while `glob0`
# takes a literal basename (so it only has to check for its existence).

def glob1(dirname, pattern):
    if not dirname:
        dirname = os.curdir
    try:
        names = os.listdir(dirname)
    except os.error:
        return []
    if pattern[0]!='.':
        names=filter(lambda x: x[0]!='.',names)
    return fnmatch.filter(names,pattern)

def glob0(dirname, basename):
    if basename == '':
        # `os.path.split()` returns an empty basename for paths ending with a
        # directory separator.  'q*x/' should match only directories.
        if os.path.isdir(dirname):
            return [basename]
    else:
        if os.path.lexists(os.path.join(dirname, basename)):
            return [basename]
    return []


magic_check = re.compile('[*?[]')

def has_magic(s):
    return magic_check.search(s) is not None
